#lang racket
; game-screen.rkt

(require
  noise
  "screen.rkt")

(provide
 (all-defined-out))

; API to use imaginary numbers as points
(define pt make-rectangular)
(define pt-x real-part)
(define pt-y imag-part)

; Convert real coordinates to canvas coordinates
(define (recenter canvas orig)
  (+ orig (pt (quotient (send canvas get-width-in-characters) 2)
              (quotient (send canvas get-height-in-characters) 2))))

(define game-screen%
  (class screen%
    ; Store the player's state
    ; Use an imaginary number for a point
    (define player (pt -12 -7))
    
    ; Get the contents of a given point, caching for future use
    ; Hash on (x y) => char
    (define caves (make-hash))
    (define (get-tile x y)
      (unless (hash-has-key? caves (list x y))
        (hash-set! caves (list x y)
                   (let ()
                     (define wall? (> (simplex (* 0.1 x) (* 0.1 y)) 0))
                     (define water? (> (simplex (* 0.1 x) 0 (* 0.1 y)) 0.5))
                     (define tree? (> (simplex 0 (* 0.1 x) (* 0.1 y)) 0.5))
                     (cond
                       [wall? 'wall]
                       [water? 'water]
                       [tree? 'tree]
                       [else 'empty]))))
      (hash-ref caves (list x y)))
    
    ; Process keyboard events
    (define/override (update key-event)
      ; Move the player
      ; NOTE: Y axis is top down, X axis is left to right
      
      ; Find where we are attempting to go
      (define target player)
      (case (send key-event get-key-code)
        [(numpad8 #\w up) (set! target (+ (pt 0 1) player))]
        [(numpad4 #\a left) (set! target (+ (pt 1 0) player))]
        [(numpad6 #\s down) (set! target (+ (pt 0 -1) player))]
        [(numpad2 #\d right) (set! target (+ (pt -1 0) player))])
      
      ; Only move if it's open
      (when (eq? 'empty (get-tile (pt-x target) (pt-y target)))
        (set! player target))
      
      ; Keep the state
      this)
    
    ; Draw the game itself.
    (define/override (draw canvas)
      (send canvas clear)
      
      ; Draw some caverns around th player
      (for* ([xi (in-range (send canvas get-width-in-characters))]
             [yi (in-range (send canvas get-height-in-characters))])
        (define x/y (recenter canvas (- player (pt xi yi))))
        (case (get-tile (pt-x x/y) (pt-y x/y))
          [(wall) (send canvas write #\# xi yi)]
          [(water) (send canvas write #\space xi yi "blue" "blue")]
          [(tree) (send canvas write #\u0005 xi yi "green")]))
             
      ; Draw the player
      ; 0x0 is the center point of the canvas
      (let ([player (recenter canvas (pt 0 0))])
        (send canvas write #\@ (pt-x player) (pt-y player))))
    
    (super-new)))